package com.wecan.base.utils;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.Md5Crypt;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

/**
 * @Author liuzhihao
 * @Date 2022.01.20
 * @Version ${version}
 * @Description ${description}
 */
public class AESUtil {
    // 字符串编码
    private static final String KEY_CHARSET = "UTF-8";

    private static final String KEY_ALGORITHM = "AES";

    private static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";

    private static int PRIVATE_KEY_SIZE_BYTE = 16;

    public static final String MYSQL_SECRET = "WEYSFAQRMMLWEYSF";

    /**
     *
     * @param secretKey 秘钥
     * @param plainText 明文
     * @return 密文
     */
    public static String encrypt(String secretKey, String plainText) {
        if (secretKey.length() != PRIVATE_KEY_SIZE_BYTE) {
            throw new RuntimeException("encrypt error, length too long");
        }
        String cipherText = "";
        try {
            Cipher cipher = initParam(secretKey, Cipher.ENCRYPT_MODE);
            byte[] bytePlainText = plainText.getBytes(KEY_CHARSET);
            byte[] byteCipherText = cipher.doFinal(bytePlainText);
            cipherText = Base64.encodeBase64String(byteCipherText);
        } catch (Exception e) {
            throw new RuntimeException("AESUtil: encrypt fail");
        }
        return cipherText;
    }

    /**
     * 加密sql中的字段值
     * @param plainText
     * @return
     */
    public static String encryptSqlField(String plainText) {
        return encrypt(MYSQL_SECRET, plainText);
    }

    /**
     *
     * @param secretKey 秘钥
     * @param cipherText 密文
     * @return 明文
     */
    public static String decrypt(String secretKey, String cipherText) {
        if (secretKey.length() != PRIVATE_KEY_SIZE_BYTE) {
            throw new RuntimeException("encrypt error, length too long");
        }
        String plainText = "";
        try {
            Cipher cipher = initParam(secretKey, Cipher.DECRYPT_MODE);
            byte[] byteCipherText = cipher.doFinal(Base64.decodeBase64(cipherText));
            plainText = new String(byteCipherText, KEY_CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("AESUtil: encrypt fail");
        }
        return plainText;
    }

    /**
     * 解密sql中的字段值
     * @param cipherText
     * @return
     */
    public static String decrypt(String cipherText) {
        return decrypt(MYSQL_SECRET, cipherText);
    }

    /**
     * 初始化参数
     * @param secretKey 秘钥
     * @param mode 加密模式
     * @return
     */
    private static Cipher initParam(String secretKey, int mode) {
        try {
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(secretKey.getBytes());
            KeyGenerator keygen = KeyGenerator.getInstance(KEY_ALGORITHM);
            keygen.init(PRIVATE_KEY_SIZE_BYTE * 8, secureRandom);
            byte[] raw = secretKey.getBytes();
            SecretKeySpec key = new SecretKeySpec(raw, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
            IvParameterSpec iv = new IvParameterSpec(raw);
            cipher.init(mode, key, iv);
            return cipher;
        } catch (Exception e) {
            throw new RuntimeException("AESUtil: initParam fail");
        }
    }

    public static void setPrivateKeySizeByte(int size) {
        PRIVATE_KEY_SIZE_BYTE = size;
    }

    /**
     * 生成一个digit位的uuid
     *
     * @param digit
     * @return
     */
    public static String generateUuid(int digit) {
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        return uuid.substring(0, digit);
    }

    public static String generateUuid() {
        return generateUuid(16);
    }

    /**
     * md5加密请求参数
     * @param map
     * @return
     */
    public static String md5Encrypt(Map<String, Object> map, boolean isUpper) {
        StringBuilder params = new StringBuilder();
        Iterator<String> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            Object value = map.get(key);
            params.append(key + "=" + value.toString() + "&");
        }
        String result = Md5Crypt.md5Crypt(params.toString().getBytes());
        if (isUpper) {
            return result.toUpperCase();
        }
        return result;
    }

    /**
     * md5加密并转换为大写
     * @param map
     * @return
     */
    public static String md5Encrypt(Map<String, Object> map) {
        return md5Encrypt(map, true);
    }
}
